import * as Koa from 'koa';
import { Route, symbolRoutePrefix } from './controller';
import { Exceptions, KLSYMBOL, loginer, RouterAttributes } from '../kalend';

/**
 * 添加静态属性
 * @prefix('user')
 */
export function prefix(prefix?: string) {
    return (target: any) => {
        if (prefix) {
        } else {
            let name: String = target.name;
            const index = name.lastIndexOf("Controller");
            if (index < 1) {
                throw new Error(`【${name}】命名错误！`)
            }
            prefix = name.substring(0, index);
        }
        if (target.__proto__.name) {
            Object.assign(target.prototype, target.__proto__.prototype);
        }
        target.prototype[symbolRoutePrefix] = prefix;
    }
};

/**
 * 路由
 * @router({
 *   method: 'get',
 *   path: '/login/:id'
 * })
 */
export function router(path: string,
    options?: {
        method?: string;
        /**
         * 是否校验用户登录  true:校验  
         */
        valid?: boolean;
    }) {
    options = Object.assign(
        {},
        {
            method: 'get',
            valid: true
        },
        options || {})
    return (target: any, name: string, property: PropertyDescriptor) => {
        RouterAttributes.router(target, name, property.value);
        let method: any = 'get';
        if (options && options.method) {
            method = options.method;
        };
        Route.__DecoratedRouters.push({
            key: name,
            target: target,
            method: method,
            path: path
        });
        if (options.valid) {
            // actionAttributeDescriptor(async (ctx: Koa.Context) => {
            //     if (ctx.headers.appid) {
            //         const loginner = ctx.session[KLSYMBOL.loginer] as loginer;
            //         if (loginner) {
            //             // console.info('已登录', ctx.url)
            //         } else {
            //             throw Exceptions.notLogedIn();
            //         }
            //     }
            // }).call({}, target, name, property);
        } else {
            console.info('不需要登录验证', target.constructor.name, path, options);
        };
    };
};

/**
 * 
 * @param callback 
 * @param options {
 * awaitlater: 等待执行完成之后再执行
 * }
 */
function actionAttributeDescriptor(callback: (_ctx: Koa.Context) => void,
    options: {
        awaitlater?: (_ctx: Koa.Context, data: any) => void
    } = {}) {
    return function (target: any, name: string, property: PropertyDescriptor) {
        RouterAttributes.add(target, name,
            async (ctx: Koa.Context, next: any) => {
                const data = await callback(ctx);
                await next();
                if (options.awaitlater) {
                    await options.awaitlater(ctx, data);
                }
            }
        );
    }
};

/**
 * url参数
 * list/:id?username=zhangsan&&age=30
 * @required({query: 'username'})
 * @required({query: ['username','age'],params: 'id'})
 */
export function required(args: any) {
    return function (target: any, name: string, descriptor: PropertyDescriptor) {
        const rules = args;
        RouterAttributes.add(
            target,
            name,
            async (ctx: Koa.Context, next: any) => {
                if (rules.query) {
                    rules.query = sureIsArray(rules.query);
                    for (let name of rules.query) {
                        if (!ctx.query[name]) {
                            throw Exceptions.known(`Request query: ${name} required`);
                        }
                    }
                };
                if (rules.params) {
                    rules.params = sureIsArray(rules.params);
                    for (let name of rules.params) {
                        if (!ctx.params[name]) {
                            throw Exceptions.known(`GET Request params: ${name} required`);
                        }
                    }
                };
                await next();
            });
    };
};

//记录请求数
let requestID = 0;
/**
 * 日志 修饰api方法
 * use: @log
 */
export const log = actionAttributeDescriptor(
    async (ctx: Koa.Context) => {
        //请求数加1
        let currentRequestID = ++requestID;
        //请求开始时间
        const startTime = process.hrtime();
        console.log(`→ (ID:${currentRequestID}) ${ctx.method} ${ctx.url}`);
        if ((ctx.method).toLowerCase() == 'post') {
            console.log(`→ (ID:${currentRequestID}) ${ctx.method} ${JSON.stringify(ctx.body)}`);
        };
        return {
            startTime: startTime,
            requestID: currentRequestID
        };
    },
    {
        awaitlater: (ctx: Koa.Context, data: any) => {
            const startTime = data.startTime;
            const currentRequestID = data.requestID;
            //返回response结束时间
            const endTime = process.hrtime();
            //计算进程总时间
            const elapsed = (endTime[0] - startTime[0]) * 1000 + (endTime[1] - startTime[1]) / 1000000;
            console.log(`← (ID:${currentRequestID}) ${ctx.method} ${ctx.url} : Status(${ctx.status}) Time(${elapsed.toFixed(0)}ms)`);
        }
    })


/**
 * 转换数组
 */
function sureIsArray(arr: any) {
    return Array.isArray(arr) ? arr : [arr];
};

// /**
//  * 修饰方法
//  * @params
//  * @convert(async function(ctx, next){await next()})
//  */
// export function convert(middleware: Function) {
//     return decorate(function (target: any, name: string, descriptor: PropertyDescriptor, middleware: Function) {
//         target[name] = sureIsArray(target[name]);
//         target[name].splice(target[name].length - 1, 0, middleware);
//         return descriptor;
//     }, sureIsArray(middleware));
// }
// /**
//  * 是否被修饰的对象原来值
//  */
// function isDescriptor(desc: PropertyDescriptor) {
//     if (!desc || !desc.hasOwnProperty) return false;
//     for (let key of ['value', 'initializer', 'get', 'set']) {
//         if (desc.hasOwnProperty(key)) return true;
//     }
//     return false;
// }

// /**
//  * 执行函数
//  */
// function decorate(handleDescriptor: Function, entryArgs: Array<Function>) {
//     if (isDescriptor(last(entryArgs))) return handleDescriptor(entryArgs);
//     else return function () {
//         return handleDescriptor(...arguments, ...entryArgs);
//     }
// }

// /**
//  * 获取第一个元素方法
//  */
// function last(arr: Array<Function>) {
//     return arr[arr.length - 1] as PropertyDescriptor;
// }

